package pl.com.qapps.datastore.berkeley.impl;

import static pl.com.qapps.datastore.berkeley.impl.EntityBindings.ENTITY_KEY_BINDING;

import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

import pl.com.qapps.datastore.KindIndex;
import pl.com.qapps.datastore.base.QueryResult;

import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.Transaction;
import com.sleepycat.bind.tuple.StringBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.bind.tuple.TupleTupleKeyCreator;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.ForeignKeyDeleteAction;
import com.sleepycat.je.SecondaryConfig;
import com.sleepycat.je.SecondaryDatabase;

/**
 * 
 * @author Jakub Dykowski
 * 
 */
public class BerkeleyKindIndex extends StoreTable implements KindIndex {

	private static final Logger logger = Logger
			.getLogger(BerkeleyKindIndex.class.getName());

	static final StringBinding KIND_KEY_BINDING = new StringBinding();

	private final SecondaryDatabase db;

	public BerkeleyKindIndex(Environment env, String filename, Database entities) {
		super(env);

		SecondaryConfig kindCfg = new SecondaryConfig();
		kindCfg.setTransactional(true);
		kindCfg.setAllowCreate(true);
		kindCfg.setSortedDuplicates(true);
		kindCfg.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
		kindCfg.setKeyCreator(new KindKeyCreator());
		// kindCfg.setBtreeComparator(COMPARATOR);
		db = env.openSecondaryDatabase(null, filename, entities, kindCfg);
	}

	@Override
	public void close() throws IOException {
		db.close();
	}

	@Override
	public QueryResult search(String kind, FetchOptions opts, Transaction txn) {
		// BerkeleyTransaction internalTxn = (BerkeleyTransaction) txn;

		DatabaseEntry keyData = new DatabaseEntry();
		KIND_KEY_BINDING.objectToEntry(kind, keyData);
		BerkeleyQuery query = new BerkeleyQuery(opts, keyData, keyData, true,
				true, true);
		// TODO transaction support needed
		return query.fetch(db, txn);
	}

	// @Override
	// public KeySource search(String kind, Transaction txn) {
	// final Queue<Key> keys = new LinkedList<Key>();
	// for (Entity entity : search(kind, (FetchOptions) null).results()) {
	// keys.add(entity.getKey());
	// }
	// return new KeySource() {
	//
	// @Override
	// public void close() throws IOException {
	//
	// }
	//
	// @Override
	// public Key next() {
	// return keys.poll();
	// }
	//
	// };
	// }

	/**
	 * Defines how keys are stored in Kind Table.
	 * 
	 * @author Jakub Dykowski
	 * 
	 */
	private static class KindKeyCreator extends TupleTupleKeyCreator<Entity> {

		public KindKeyCreator() {
		}

		@Override
		public boolean createSecondaryKey(TupleInput primaryKeyInput,
				TupleInput dataInput, TupleOutput indexKeyOutput) {
			Key entityKey = ENTITY_KEY_BINDING.entryToObject(primaryKeyInput);
			KIND_KEY_BINDING.objectToEntry(entityKey.getKind(), indexKeyOutput);
			// old
			// indexKeyOutput.writeBytes(ENTITY_KEY_BINDING.entryToObject(
			// primaryKeyInput).getKind());
			logger.log(Level.FINE, "creating kind index key: " + entityKey
					+ " => " + entityKey.getKind());
			return true;
		}

		// very old
		// @Override
		// public boolean createSecondaryKey(TupleInput primaryKeyInput,
		// Entity dataInput, TupleOutput indexKeyOutput) {
		// // System.out.println("KindKey: creating secondary key: " +
		// // dataInput.getKind());
		// indexKeyOutput.writeString(dataInput.getKind());
		// return true;
		// }

	}

}
